home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-09-16 | 73.5 KB | 2,747 lines | [TEXT/MMCC] |
- /*----------------------------------------------------------------------------
-
- net.c
-
- This reusable and reentrant module handles low-level communications with
- TCP/IP network servers, using a simple "net ASCII" command/response
- stream model. It also exports several functions for using FTP data streams,
- and several functions for doing domain name resolver tasks.
-
- The module hides the complexity and peculiarities of Open Transport and the
- MacTCP device driver. If Open Transport is available and we are running in
- native PowerPC mode, it is used. Otherwise, MacTCP is used.
-
- The module also manages many of the common protocol details of communicating
- with net ASCII servers:
-
- Handling initial server "hello" messages.
- Handling final server "QUIT" commands and all the details of graceful
- asynchronous stream close operations.
- Mapping between CR line terminators and CRLF line terminators.
- Decoding numeric server response codes.
- Skipping server comments (response lines with "-" following the response code).
- Supplying terminating "." lines when sending blocks of text.
- Recognizing and discarding terminating "." lines when reading blocks of text.
- Mapping between leading "." characters and leading ".." characters on text lines.
-
- The module emphasizes simplicity at the expense of power. There are many things
- you can do by calling Open Transport or MacTCP directly which you cannot do with
- this module. The module is *not* a general purpose interface to Open Transport or
- MacTCP designed to meet the needs of all possible Mac TCP/IP networking programs.
- It is only a convenient interface for writing clients for typical simple net ASCII
- servers.
-
- The following mandatory functions handle initialization, idle time, and
- termination tasks.
-
- NetInit - Initialize the module.
- NetIdle - Handle idle time tasks.
- NetTerm - Terminate the module.
-
- The following functions work with Net ASCII command/response streams.
-
- NetOpen - Open a stream.
- NetClose - Close a stream.
- NetCommand - Send a command and get the response.
- NetGetExtraResponse - Get an extra command response.
- NetBatchedCommands - Send several commands in a batch and
- get and process the responses.
- NetPutText - Send command text.
- NetGetText - Get response text.
-
- The following functions work with FTP data streams. They are used by
- the ftp.c module.
- .
- NetFTPDataPassiveOpen - Open an FTP passive data stream.
- NetFTPDataClose - Close an FTP data stream.
- NetFTPDataWaitForConnection - Wait for the FTP server to open
- its end of an FTP passive data stream.
- NetGetFTPData - Get FTP data.
- NetSendFTPData - Send FTP data.
-
- The following functions handle simple domain name resolver and related tasks.
-
- NetGetMyAddr - Get my IP address.
- NetGetMyAddrStr - Get my IP address as a dotted-decimal string.
- NetGetMyName - Get my domain name.
- NetNameToAddr - Convert a domain name to an IP address.
- NetAddrToName - Convert an IP address to a domain name.
-
- Finally, the module exports the following utility functions:
-
- NetMacTCPDNROperationInProgress - Determine if a MacTCP DNR operation
- is in progress.
- NetGetServerErrInfo - Get server error information.
- NetGetStreamStats - Get stream stats (bytes in/out).
- NetHaveOT - Determine whether we are using Open Transport or MacTCP.
-
- A "stream" is an abstraction representing a bidirectional network connection
- to a net ASCII TCP/IP server.
-
- With Open Transport, the notion of a "stream" in this module is basically
- equivalent to a "TCP endpoint". The "NetOpen" function opens an endpoint,
- binds it to a TCP port, and creates a connection. The "NetClose" function
- does an orderly disconnect and closes the endpoint.
-
- With MacTCP, the notion of a "stream" in this module combines the
- separate MacTCP notions of "stream" and "connection". When calling MacTCP
- directly, you "create" and "release" streams and "open" and "close" connections.
- These operations are combined in the net.c module. The "NetOpen" function both
- creates a stream and opens a connection. The "NetClose" function both
- closes the connection and releases the stream.
-
- A stream is represented as a variable of type "NetStreamRef". These stream
- references are opaque. You may copy them and pass them as parameters to
- functions in net.c, but you are prohibited from accessing the contents of
- the memory blocks pointed to by the references. Only the functions in
- net.c are permitted to manipulate the contents of these blocks.
-
- The functions return a value of type OSErr as the function result:
-
- noErr no error occurred
- netOpenDriverErr NetInit could not initialize the network driver
- netOpenStreamErr stream open failed
- netLostConnectionErr stream was unexpectedly closed or aborted by server
- netDNRErr DNR failed to resolve name or address
- netTruncatedErr incoming text was truncated
- other any other OS or toolbox error
-
- If any error occurs, the stream is aborted before returning to the caller.
- "Aborted" means that the server connection is closed abruptly, without going
- through the usual orderly TCP stream teardown process. The stream is also
- deallocated. In this case, you must not attempt to reuse the stream reference,
- since the stream no longer exists. You must perform careful error checking.
-
- The "expected" Open Transport and MacTCP error codes are translated into either
- regular MacOS error codes (e.g., kENOMEMErr -> memFullErr) or into the
- special "net" error codes (e.g., authNameErr -> netDNRErr). This
- gives the clients of this module a uniform set of error codes which are the
- same for both Open Transport and MacTCP. Open Transport and MacTCP error
- codes which are not translated are "unexpected" errors which should not
- occur, and if they do, indicate an error in the code in this module.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdio.h>
-
- #include "MyMacTCP.h"
-
- #include "OpenTransport.h"
- #include "OpenTptInternet.h"
-
- #include "def.h"
- #include "net.h"
- #include "memutil.h"
- #include "strutil.h"
-
-
-
- /* Constants. */
-
- #define kBufSize (4*1024) /* buffer size */
- #define kBufSizeDiv2 (kBufSize/2) /* buffer size div 2 */
- #define kMinTCPBufSize (16*1024) /* minimum MacTCP stream buffer size */
-
-
-
- /* Types. */
-
- typedef struct TStream { /* Stream info */
-
- TCPiopb pBlock; /* MacTCP parameter block (must be first, unused with
- Open Transport) */
-
- /* Fields used with both Open Transport and MacTCP. */
-
- struct TStream *next; /* pointer to next stream in list of all open streams */
- struct TStream *nextClose; /* pointer to next stream in list of closing streams */
- struct TStream *nextFree; /* pointer to next stream in list of available preallocated
- streams */
- unsigned long serverAddr; /* IP address of server */
- unsigned short serverPort; /* port number on server */
- unsigned short localPort; /* local port number */
- char buf[kBufSize]; /* data buffer */
- char *in; /* pointer to next location in data buffer in
- which incoming network bytes should be stored */
- char *out; /* pointer to next location in data buffer from
- which bytes should be removed and delivered
- to our clients (note: we always have out <= in) */
- Boolean fromFreeList; /* true if this buffer was obtained from the free list */
- CStr255 command; /* last command sent on stream */
- CStr255 response; /* last response received on stream */
- long responseCode; /* last response code received on stream */
- Boolean closing; /* true when closing stream */
- Boolean otherSideHasClosed; /* true when other side has closed its end of
- the stream */
- Boolean weHaveClosed; /* true when we have closed our end of the stream */
- Boolean release; /* true when stream should be released */
- long bytesIn; /* number of bytes received on stream */
- long bytesOut; /* number of bytes sent on stream */
-
- /* Fields used only with Open Transport. */
-
- EndpointRef ref; /* endpoint reference */
- Boolean complete; /* true when asynch operation has completed */
- OTEventCode code; /* event code */
- OTResult result; /* result */
- void *cookie; /* cookie */
- TCall *call; /* pointer to call structure */
- TBind *bindReq; /* pointer to bind request structure */
- TBind *bindRet; /* pointer to bind return structure */
-
- /* Fields used only with MacTCP. */
-
- long myA5; /* our A5 register */
- StreamPtr tcpStream; /* MacTCP stream pointer */
- struct wdsEntry wds[2]; /* MacTCP write data structure */
- char mactcpBuf[1]; /* MacTCP buffer - size depends on MTU (must be last) */
-
- } TStream, *TStreamPtr;
-
- typedef struct TMyOTInetSvcInfo { /* Open Transport Internet services provider info */
- InetSvcRef ref; /* provider reference */
- Boolean complete; /* true when asynch operation has completed */
- OTResult result; /* result code */
- void *cookie; /* cookie */
- } TMyOTInetSvcInfo;
-
-
-
- /* Global variables. */
-
- static Boolean gHaveOT; /* true if we have Open Transport */
- static NetGiveTimeFunction gGiveTime; /* pointer to GiveTime function */
- static NetLogFunction gLog; /* pointer to logging function, or nil if none */
- static TStreamPtr gAll = nil; /* pointer to list of open streams */
- static TStreamPtr gClose = nil; /* pointer to list of closing streams */
- static TStreamPtr gFree = nil; /* pointer to list of available preallocated
- stream buffers */
- static long gStreamBufSize; /* stream buffer size */
-
- static short gRefNum; /* MacTCP driver reference number */
- static long gTCPBufSize; /* TCP buffer size for MacTCP */
- static Boolean gMacTCPDNROperationInProgress = false;
- /* True when MacTCP DNR operation in progress */
-
- static ResultUPP gMacTCPDNRResultProcUPP = nil;
- static TCPIOCompletionUPP gMacTCPCloseStreamCompletionRoutineUPP = nil;
- static UniversalProcPtr gOldExitToShellUPP;
- static UniversalProcPtr gMyExitToShellUPP;
-
-
-
- /*----------------------------------------------------------------------------
- TranslateErrorCode
-
- Translate error codes.
-
- Entry: err = Open Transport, MacTCP, or OS error code.
-
- Exit: function result = translated error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr TranslateErrorCode (OSErr err)
- {
- switch (err) {
- case kENOMEMErr:
- return memFullErr;
- case noNameServer:
- case authNameErr:
- case noAnsErr:
- case dnrErr:
- case nameSyntaxErr:
- return netDNRErr;
- case openFailed:
- return netOpenStreamErr;
- default:
- return err;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- LogMessage
-
- Log a message.
-
- Entry: s = pointer to stream.
- logEntryType =
- 'C' if command.
- 'R' if response.
- ' ' if open/close operation.
- str = command or response string or open/close message string.
- ----------------------------------------------------------------------------*/
-
- static void LogMessage (TStreamPtr s, char logEntryType, char *str)
- {
- if (gLog != nil) (*gLog)(logEntryType, s->serverAddr, s->serverPort,
- s->localPort, str);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NewStreamBuffer
-
- Allocate a stream buffer.
-
- Exit: function result = error code.
- *s = pointer to new stream buffer.
- ----------------------------------------------------------------------------*/
-
- static OSErr NewStreamBuffer (TStreamPtr *s)
- {
- OSErr err = noErr;
-
- if (gFree == nil) {
- err = MyNewPtr(gStreamBufSize, s);
- if (err != noErr) return err;
- } else {
- *s = gFree;
- gFree = gFree->nextFree;
- memset(*s, 0, sizeof(TStream));
- (*s)->fromFreeList = true;
- }
- if (!gHaveOT) (*s)->myA5 = SetCurrentA5();
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DisposeStreamBuffer
-
- Dispose a stream buffer.
-
- Entry: s = pointer to stream buffer.
- ----------------------------------------------------------------------------*/
-
- static void DisposeStreamBuffer (TStreamPtr s)
- {
- if (s == nil) return;
-
- if (gHaveOT) {
- if (s->call != nil) OTFree(s->call, T_CALL);
- if (s->bindReq != nil) OTFree(s->bindReq, T_BIND);
- if (s->bindRet != nil) OTFree(s->bindRet, T_BIND);
- }
-
- if (s->fromFreeList) {
- s->nextFree = gFree;
- gFree = s;
- } else {
- MyDisposePtr(s);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyOTStreamNotifyProc
-
- Open Transport notifier proc for TCP streams.
-
- Entry: s = pointer to stream.
- code = OT event code.
- result = OT result.
- cookie = OT cookie.
- ----------------------------------------------------------------------------*/
-
- static pascal void MyOTStreamNotifyProc (TStreamPtr s, OTEventCode code,
- OTResult result, void *cookie)
- {
- switch (code) {
- case T_DISCONNECT:
- /* Other side has aborted. */
- s->otherSideHasClosed = true;
- s->complete = true;
- break;
- case T_ORDREL:
- /* Other side has closed. Close our side if necessary. */
- s->otherSideHasClosed = true;
- s->complete = true;
- OTRcvOrderlyDisconnect(s->ref);
- if (!s->weHaveClosed) {
- OTSndOrderlyDisconnect(s->ref);
- s->weHaveClosed = true;
- }
- if (s->closing) s->release = true;
- break;
- case T_DATA:
- if (s->closing) {
- /* Consume and discard response to "QUIT" comand. */
- do {
- result = OTRcv(s->ref, s->buf, kBufSize, nil);
- } while (result >= 0);
- }
- break;
- case T_OPENCOMPLETE:
- case T_BINDCOMPLETE:
- case T_CONNECT:
- case T_PASSCON:
- s->complete = true;
- s->code = code;
- s->result = result;
- s->cookie = cookie;
- break;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyOTStreamWait
-
- Wait for an asynchronous Open Transport stream call to complete.
-
- Entry: s = pointer to stream.
- returnImmediatelyOnError = true to return immediately if
- the GiveTime function returns an error.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr MyOTStreamWait (TStreamPtr s, Boolean returnImmediatelyOnError)
- {
- OSErr err = noErr, giveTimeErr = noErr;
-
- do {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- if (err != noErr && returnImmediatelyOnError) return err;
- } while (!s->complete);
- if (err == noErr) err = s->result;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyOTInetSvcNotifyProc
-
- Open Transport notifier proc for an Internet services provider.
-
- Entry: svcIfno = pointer to MyOTInetSvcInfo struct.
- code = OT event code.
- result = OT result.
- cookie = OT cookie.
- ----------------------------------------------------------------------------*/
-
- static pascal void MyOTInetSvcNotifyProc (TMyOTInetSvcInfo *svcInfo, OTEventCode code,
- OTResult result, void *cookie)
- {
- switch (code) {
- case T_OPENCOMPLETE:
- case T_DNRSTRINGTOADDRCOMPLETE:
- case T_DNRADDRTONAMECOMPLETE:
- svcInfo->complete = true;
- svcInfo->result = result;
- svcInfo->cookie = cookie;
- break;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyOTInetSvcWait
-
- Wait for an asynchronous Open Transport Internet services call to complete.
-
- Entry: svcInfo = pointer to TMyOTInetSvcInfo struct.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr MyOTInetSvcWait (TMyOTInetSvcInfo *svcInfo)
- {
- OSErr err = noErr;
-
- do {
- err = (*gGiveTime)();
- if (err != noErr) return err;
- } while (!svcInfo->complete);
- return svcInfo->result;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyOTOpenInetServices
-
- Open an Internet services provider.
-
- Entry: svcInfo = pointer to TMyOTInetSvcInfo struct for this
- provider.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr MyOTOpenInetServices (TMyOTInetSvcInfo *svcInfo)
- {
- OSErr err = noErr;
-
- svcInfo->complete = false;
- err = OTAsyncOpenInternetServices(kDefaultInternetServicesPath, 0,
- MyOTInetSvcNotifyProc, svcInfo);
- if (err != noErr) return err;
- err = MyOTInetSvcWait(svcInfo);
- if (err != noErr) return err;
- svcInfo->ref = svcInfo->cookie;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- InitMacTCPParamBlock
-
- Initialize a MacTCP parameter block.
-
- Entry: pBlock = pointer to parameter block.
- csCode = MacTCP control code (TCPCreate, etc.).
- ----------------------------------------------------------------------------*/
-
- static void InitMacTCPParamBlock (TCPiopb *pBlock, short csCode)
- {
- memset(pBlock, 0, sizeof(TCPiopb));
- pBlock->ioResult = 1;
- pBlock->ioCRefNum = gRefNum;
- pBlock->csCode = csCode;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CallMacTCP
-
- Call MacTCP and wait for the call to complete.
-
- Entry: s = pointer to stream.
- returnImmediatelyOnError = true to return immediately if
- the GiveTime function returns an error.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr CallMacTCP (TStreamPtr s, Boolean returnImmediatelyOnError)
- {
- OSErr err = noErr, giveTimeErr = noErr;
-
- PBControlAsync((ParmBlkPtr)&s->pBlock);
- do {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- if (err != noErr && returnImmediatelyOnError) return err;
- } while (s->pBlock.ioResult > 0);
- if (err == noErr) err = s->pBlock.ioResult;
- if (err == connectionClosing || err == connectionDoesntExist ||
- err == connectionTerminated) s->otherSideHasClosed = true;
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MacTCPDNRResultProc
-
- MacTCP domain name resolver completion routine.
-
- Entry: hInfoPtr = pointer to hostInfo struct.
- userDataPtr = pointer to user data.
- ----------------------------------------------------------------------------*/
-
- static pascal void MacTCPDNRResultProc (struct hostInfo *hInfoPtr, char *userDataPtr)
- {
- *(Boolean*)userDataPtr = true;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MacTCPCloseStreamCompletionRoutine
-
- This is the asynchronous MacTCP completion routine used to close streams.
-
- This completion routine chains to itself to do the following tasks
- involved in gracefully tearing down a stream in the background:
-
- Wait for QUIT command send to complete (the send is initiated by the
- NetClose function below).
- Read incoming data until an error occurs (signalling that the
- server has closed its end of the connection).
- Close our end of the connection.
-
- Entry: pBlock = pointer to MacTCP parameter block.
- ----------------------------------------------------------------------------*/
-
- static void MacTCPCloseStreamCompletionRoutine (TCPiopb *pBlock)
- {
- TStreamPtr s;
- OSErr err = noErr;
- long savedA5;
-
- s = (TStreamPtr)pBlock;
-
- savedA5 = SetA5(s->myA5);
-
- if (s->pBlock.csCode == TCPSend) {
-
- /* The QUIT command has been sent. Start the first receive. */
-
- err = s->pBlock.ioResult;
- if (err != noErr) {
- s->release = true;
- SetA5(savedA5);
- return;
- }
- InitMacTCPParamBlock(&s->pBlock, TCPRcv);
- s->pBlock.csParam.receive.rcvBuff = s->buf;
- s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
-
- } else if (s->pBlock.csCode == TCPRcv) {
-
- /* A receive operation has finished. If there was no error, start another
- receive. If an error occurred, it was because the server has closed
- its side of the connection. In this case, if we have already closed
- our side of the connection, set the "release" flag to signal that
- we can now release the stream and dispose of the queue element. If
- we have not yet closed our side, we start the TCPClose. */
-
- err = s->pBlock.ioResult;
- if (err == noErr) {
- InitMacTCPParamBlock(&s->pBlock, TCPRcv);
- s->pBlock.csParam.receive.rcvBuff = s->buf;
- s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
- } else {
- s->otherSideHasClosed = true;
- if (s->weHaveClosed) {
- s->release = true;
- SetA5(savedA5);
- return;
- } else {
- InitMacTCPParamBlock(&s->pBlock, TCPClose);
- }
- }
-
- } else if (s->pBlock.csCode == TCPClose) {
-
- /* Our close has finished. If the other side has also closed,
- set the "release" flag to signal that we can now release
- the stream and dispose of the queue element. If the other
- side has not yet closed, issue a receive operation and wait
- for the other side to close. */
-
- s->weHaveClosed = true;
- if (s->otherSideHasClosed) {
- s->release = true;
- SetA5(savedA5);
- return;
- } else {
- InitMacTCPParamBlock(&s->pBlock, TCPRcv);
- s->pBlock.csParam.receive.rcvBuff = s->buf;
- s->pBlock.csParam.receive.rcvBuffLen = kBufSize;
- }
-
- }
-
- /* Issue the next PBControl call in the chain. */
-
- s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.ioResult = 1;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) s->release = true;
- SetA5(savedA5);
-
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetMaxTCPMTU
-
- Get the max MTU size.
-
- Exit: function result = max MTU, or 0 if error.
- ----------------------------------------------------------------------------*/
-
- static long GetMaxTCPMTU (void)
- {
- UDPiopb iopb;
- OSErr err = noErr;
-
- memset(&iopb, 0, sizeof(iopb));
- err = NetGetMyAddr(&iopb.csParam.mtu.remoteHost);
- if (err != noErr) return 0;
- iopb.ioCRefNum = gRefNum;
- iopb.csCode = UDPMaxMTUSize;
- err = PBControlSync((ParmBlkPtr)&iopb);
- if (err != noErr) return 0;
- return iopb.csParam.mtu.mtuSize;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPCreate
-
- Create a stream.
-
- Entry: s = pointer to stream.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPCreate (TStreamPtr s)
- {
- OSErr err = noErr;
- OSStatus oterr;
-
- if (gHaveOT) {
-
- s->complete = false;
- err = OTAsyncOpenEndpoint(OTCreateConfiguration(kTCPName), 0,
- nil, MyOTStreamNotifyProc, s);
- if (err != noErr) return err;
- err = MyOTStreamWait(s, false);
- if (err != noErr) return err;
- if (s->code != T_OPENCOMPLETE) return netOpenStreamErr;
- s->ref = s->cookie;
- s->call = OTAlloc(s->ref, T_CALL, T_ADDR, &oterr);
- err = oterr;
- if (err != noErr) goto exit;
- s->bindReq = OTAlloc(s->ref, T_BIND, T_ADDR, &oterr);
- err = oterr;
- if (err != noErr) goto exit;
- s->bindRet = OTAlloc(s->ref, T_BIND, T_ADDR, &oterr);
- err = oterr;
- if (err != noErr) goto exit;
-
- } else {
-
- InitMacTCPParamBlock(&s->pBlock, TCPCreate);
- s->pBlock.csParam.create.rcvBuff = s->mactcpBuf;
- s->pBlock.csParam.create.rcvBuffLen = gTCPBufSize;
- err = CallMacTCP(s, false);
- if (err != noErr) return err;
- s->tcpStream = s->pBlock.tcpStream;
- }
-
- s->next = gAll;
- gAll = s;
- return noErr;
-
- exit:
-
- OTCloseProvider(s->ref);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPRelease
-
- Release a stream.
-
- Entry: s = pointer to stream.
-
- Exit: function result = error code.
-
- Any active connection is also aborted, if necessary, before releasing
- the stream.
-
- With MacTCP, the function uses its own TCPiopb parameter block instead
- of the one inside the stream block, because the one inside the stream block
- may already be in use by some other asynchronous operation.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPRelease (TStreamPtr s)
- {
- TCPiopb pBlock;
- OSErr err = noErr, giveTimeErr = noErr;
- TStreamPtr xs, next, prev = nil;
- Boolean abort;
-
- if (s == nil) return noErr;
- abort = !s->otherSideHasClosed || !s->weHaveClosed;
-
- if (gHaveOT) {
-
- if (abort) OTSndDisconnect(s->ref, nil);
- err = OTCloseProvider(s->ref);
-
- } else {
-
- InitMacTCPParamBlock(&pBlock, TCPRelease);
- pBlock.tcpStream = s->tcpStream;
- PBControlAsync((ParmBlkPtr)&pBlock);
- while (pBlock.ioResult > 0) {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- }
- if (err == noErr) err = pBlock.ioResult;
- }
-
- for (xs = gAll; xs != nil; xs = next) {
- next = xs->next;
- if (xs == s) {
- if (prev == nil) {
- gAll = next;
- } else {
- prev->next = next;
- }
- break;
- }
- prev = xs;
- }
-
- if (gLog != nil) LogMessage(s, ' ', abort ? "Stream aborted." : "Stream released.");
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPActiveOpen
-
- Open an active stream.
-
- Entry: s = pointer to stream.
- addr = IP address of server.
- port = port number of service.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPActiveOpen (TStreamPtr s, unsigned long addr,
- unsigned short port)
- {
- OSErr err = noErr;
- InetAddress *inetAddr;
-
- if (gHaveOT) {
-
- s->complete = false;
- err = OTBind(s->ref, nil, s->bindRet);
- if (err != noErr) return err;
- err = MyOTStreamWait(s, true);
- if (err != noErr) return err;
- if (s->code != T_BINDCOMPLETE) return netOpenStreamErr;
- inetAddr = (InetAddress*)s->bindRet->addr.buf;
- s->localPort = inetAddr->fPort;
- OTInitInetAddress((InetAddress*)s->call->addr.buf, port, addr);
- s->call->addr.len = sizeof(InetAddress);
- s->complete = false;
- err = OTConnect(s->ref, s->call, nil);
- if (err != noErr && err != kOTNoDataErr)
- return s->otherSideHasClosed ? netOpenStreamErr : err;
- err = MyOTStreamWait(s, true);
- if (err != noErr)
- return s->otherSideHasClosed ? netOpenStreamErr : err;
- if (s->code != T_CONNECT) return netOpenStreamErr;
- err = OTRcvConnect(s->ref, nil);
- if (err != noErr) return err;
-
- } else {
-
- InitMacTCPParamBlock(&s->pBlock, TCPActiveOpen);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.open.remoteHost = addr;
- s->pBlock.csParam.open.remotePort = port;
- err = CallMacTCP(s, true);
- if (err == noErr) {
- s->localPort = s->pBlock.csParam.open.localPort;
- } else {
- return s->otherSideHasClosed ? netOpenStreamErr : err;
- }
- }
-
- if (gLog != nil) LogMessage(s, ' ', "Stream opened.");
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPPassiveOpen
-
- Open a passive stream.
-
- Entry: s = pointer to stream.
-
- Exit: function result = error code.
- *port = assigned unused local port number.
-
- Note: Unlike the other "DoTCPxxx" functions, DoTCPPassiveOpen is
- asynchronous. The passive stream is opened, but the function does not
- wait for another host to connect. The function is used by
- NetFTPDataPassiveOpen to open passive FTP data streams. The caller must
- call NetFTPDataWaitForConnection to wait for the FTP server to connect
- to the stream.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPPassiveOpen (TStreamPtr s, unsigned short *port)
- {
- OSErr err = noErr;
- InetAddress *inetAddr;
-
- if (gHaveOT) {
-
- s->bindReq->addr.len = 0;
- s->bindReq->qlen = 1;
- s->complete = false;
- err = OTBind(s->ref, s->bindReq, s->bindRet);
- if (err != noErr) return err;
- err = MyOTStreamWait(s, true);
- if (err != noErr) return err;
- if (s->code != T_BINDCOMPLETE) return netOpenStreamErr;
- inetAddr = (InetAddress*)s->bindRet->addr.buf;
- *port = s->localPort = inetAddr->fPort;
- return noErr;
-
- } else {
-
- InitMacTCPParamBlock(&s->pBlock, TCPPassiveOpen);
- s->pBlock.tcpStream = s->tcpStream;
- PBControlAsync((ParmBlkPtr)&s->pBlock);
- while (s->pBlock.csParam.open.localPort == 0) {
- err = (*gGiveTime)();
- if (err != noErr) return err;
- }
- *port = s->localPort = s->pBlock.csParam.open.localPort;
- return noErr;
-
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPSend
-
- Send data on a stream.
-
- Entry: s = pointer to stream.
- data = pointer to data to send.
- len = length of data to send.
- push = true to set push data flag. This flag should be set on
- the last buffer of data being sent on the stream before
- a response from the server is expected. This flag is need
- to keep IBM VM/CMS happy. (Note OT handles this internally,
- so this is needed only with MacTCP.)
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPSend (TStreamPtr s, Ptr data, unsigned short len, Boolean push)
- {
- OSErr err = noErr;
- OTResult result;
-
- s->in = s->out = s->buf;
-
- if (gHaveOT) {
-
- while (len > 0) {
- err = (*gGiveTime)();
- if (err != noErr) return err;
- result = OTSnd(s->ref, data, len, 0);
- if (result >= 0) {
- len -= result;
- s->bytesOut += result;
- } else if (result != kOTFlowErr) {
- return s->otherSideHasClosed ? netLostConnectionErr : result;
- }
- }
- return noErr;
-
- } else {
-
- s->wds[0].ptr = data;
- s->wds[0].length = len;
- InitMacTCPParamBlock(&s->pBlock, TCPSend);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
- s->pBlock.csParam.send.pushFlag = push;
- err = CallMacTCP(s, true);
- if (err == noErr) s->bytesOut += len;
- return s->otherSideHasClosed ? netLostConnectionErr : err;
-
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- DoTCPRcv
-
- Reveive data on a stream.
-
- Entry: s = pointer to stream.
- data = pointer to data buffer.
- *len = length of data buffer.
-
- Exit: function result = error code.
- *len = number of bytes received.
- ----------------------------------------------------------------------------*/
-
- static OSErr DoTCPRcv (TStreamPtr s, Ptr data, unsigned short *len)
- {
- OSErr err = noErr;
- OTResult result;
-
- if (gHaveOT) {
-
- err = (*gGiveTime)();
- if (err != noErr) return err;
- result = OTRcv(s->ref, data, *len, nil);
- if (result >= 0) {
- *len = result;
- s->bytesIn += result;
- return noErr;
- } else if (s->otherSideHasClosed) {
- return netLostConnectionErr;
- } else if (result == kOTNoDataErr) {
- *len = 0;
- return noErr;
- } else {
- return result;
- }
-
- } else {
-
- InitMacTCPParamBlock(&s->pBlock, TCPRcv);
- s->pBlock.tcpStream = s->tcpStream;
- s->pBlock.csParam.receive.rcvBuff = StripAddress(data);
- s->pBlock.csParam.receive.rcvBuffLen = *len;
- err = CallMacTCP(s, true);
- *len = s->pBlock.csParam.receive.rcvBuffLen;
- if (err == noErr) s->bytesIn += *len;
- return err != noErr && s->otherSideHasClosed ? netLostConnectionErr : err;
-
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyExitToShell
-
- ExitToShell trap patch (for MacTCP only).
-
- This patch makes sure that all open streams are closed when ExitToShell
- is called (e.g., if the program crashes and you type "es" in MacsBug, or
- if you use Command-Option-Escape to force quit the program). This keeps
- MacTCP happy. It doesn't always work, but it helps sometimes.
-
- Open Transport has its own mechanisms for doing this, so this patch
- is only installed if we are using MacTCP.
- ----------------------------------------------------------------------------*/
-
- static void MyExitToShell (void)
- {
- TStreamPtr s;
-
- SetCurrentA5();
- SetToolTrapAddress(gOldExitToShellUPP, _ExitToShell);
- gLog = nil;
- for (s = gAll; s != nil; s = s->next) DoTCPRelease(s);
- ExitToShell();
- }
-
-
-
- /*----------------------------------------------------------------------------
- PatchExitToShell
-
- Install a patch on the ExitToShell trap (for MacTCP only).
- ----------------------------------------------------------------------------*/
-
- static void PatchExitToShell (void)
- {
- gOldExitToShellUPP = GetToolTrapAddress(_ExitToShell);
- gMyExitToShellUPP =
- NewRoutineDescriptor((ProcPtr)MyExitToShell, kPascalStackBased, GetCurrentISA());
- SetToolTrapAddress(gMyExitToShellUPP, _ExitToShell);
- }
-
-
-
- /*----------------------------------------------------------------------------
- SendCommand
-
- Send a command on a stream.
-
- Entry: s = pointer to stream.
- command = C-format command string.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- static OSErr SendCommand (TStreamPtr s, char *command)
- {
- OSErr err = noErr;
- unsigned short len;
-
- strcpy(s->command, command);
- len = strlen(command);
- BlockMoveData(command, s->buf, len);
- *(s->buf + len) = CR;
- *(s->buf + len + 1) = LF;
- err = DoTCPSend(s, s->buf, len+2, true);
- if (err != noErr) return err;
- if (gLog != nil) LogMessage(s, 'C', command);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- ReadResponse
-
- Read a command response from a stream.
-
- Entry: s = pointer to stream.
-
- Exit: function result = error code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- static OSErr ReadResponse (TStreamPtr s, long *responseCode, CStr255 response)
- {
- char *in, *out, *p, *q, *r;
- unsigned short len, numUnread;
- long num;
- OSErr err = noErr;
-
- in = s->in;
- out = s->out;
-
- while (true) {
- while (out < in) {
- for (p = out; p < in; p++) {
- if (*p == LF) {
- *p = 0;
- q = out;
- out = p+1;
- break;
- } else if (*p == CR && p < in-1 && *(p+1) == LF) {
- *p = 0;
- q = out;
- out = p+2;
- break;
- }
- }
- if (p >= in) break;
- r = q;
- if (*r == '-') {
- r++;
- num = -CrackNum(&r);
- } else {
- num = CrackNum(&r);
- }
- if (*r != '-' && num != 0) {
- if (p - q > 255) *(q+255) = 0;
- strcpy(response, q);
- strcpy(s->response, q);
- *responseCode = s->responseCode = num;
- s->in = in;
- s->out = out;
- if (gLog != nil) LogMessage(s, 'R', response);
- return noErr;
- }
- }
- if (out == in) {
- out = in = s->buf;
- } else if (in > s->buf + kBufSizeDiv2) {
- numUnread = in - out;
- if (numUnread > kBufSizeDiv2) {
- numUnread = kBufSizeDiv2;
- out = in - numUnread;
- }
- BlockMoveData(out, s->buf, numUnread);
- out = s->buf;
- in = out + numUnread;
- }
- len = s->buf + kBufSize - in;
- err = DoTCPRcv(s, in, &len);
- if (err != noErr) return err;
- in += len;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MungeOut
-
- Munge a block of text before sending it out to a server.
-
- Entry: text = handle to CR-terminated ASCII text lines.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- mungePeriods = true to munge periods.
-
- Exit: function result = error code.
-
- CR line terminators are translated to CRLF. If the text block does
- not have a terminating CR at the end of the last line, one is added.
-
- If mungePeriods=true, leading "." characters on lines are mapped to
- "..", and a terminating "." on a line by itself is added to the end
- of the text block.
- ----------------------------------------------------------------------------*/
-
- static OSErr MungeOut (Handle text, Boolean mungePeriods)
- {
- OSErr err = noErr;
- char *p, *pEnd, *pStart, *q;
- long textLen, oldTextLen;
-
- textLen = MyGetHandleSize(text);
- p = *text;
- pEnd = p + textLen;
- if (textLen == 0 || *(pEnd-1) != CR) {
- textLen++;
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- p = *text;
- pEnd = p + textLen;
- *(pEnd-1) = CR;
- }
- oldTextLen = textLen;
- if (mungePeriods) {
- while (p < pEnd) {
- if (*p == '.') textLen++;
- while (*p != CR) p++;
- p++;
- textLen++;
- }
- textLen += 3;
- } else {
- for (; p < pEnd; p++) if (*p == CR) textLen++;
- }
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- pStart = *text;
- p = pStart + oldTextLen - 1;
- q = pStart + textLen - 1;
- if (mungePeriods) {
- *q-- = LF;
- *q-- = CR;
- *q-- = '.';
- while (p >= pStart) {
- *q-- = LF;
- *q-- = CR;
- p--;
- while (*p != CR && p >= pStart) *q-- = *p--;
- if (*(p+1) == '.') *q-- = '.';
- }
- } else {
- while (p >= pStart) {
- if (*p == CR) *q-- = LF;
- *q-- = *p--;
- }
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MungeIn
-
- Munge a block of text after receiving it from a server.
-
- Entry: text = handle to ASCII text lines as received from server.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- textLen = number of characters in text block.
- mungePeriods = true to munge periods.
-
- Exit: function result = error code.
-
- CRLF line terminators are translated to CR. A final CR is added at the
- end of the last line if necessary.
-
- If mungePeriods=true, leading ".." characters on lines are mapped to
- ".", and the terminating "." on a line by itself is removed from the end
- of the text block.
- ----------------------------------------------------------------------------*/
-
- static OSErr MungeIn (Handle text, long textLen, Boolean mungePeriods)
- {
- OSErr err = noErr;
- char *pEnd, *p, *q;
- Boolean needTerminatingCR = false;
-
- if (mungePeriods && textLen >= 3) {
- p = *text + textLen - 3;
- if (*p == '.' && *(p+1) == CR && *(p+2) == LF) textLen -= 3;
- }
- p = q = *text;
- pEnd = p + textLen;
- if (mungePeriods) {
- while (p < pEnd) {
- if (*p == '.' && *(p+1) == '.') {
- *q++ = '.';
- p += 2;
- }
- while (p < pEnd && (*p != CR || *(p+1) != LF)) *q++ = *p++;
- *q++ = CR;
- p += 2;
- }
- } else {
- while (p < pEnd) {
- if (*p == CR && *(p+1) == LF) {
- *q++ = CR;
- p += 2;
- } else {
- *q++ = *p++;
- }
- }
- }
- textLen = q - *text;
- if (textLen > 0 && *(q-1) != CR) {
- textLen++;
- needTerminatingCR = true;
- }
- err = MySetHandleSize(text, textLen);
- if (err != noErr) return err;
- if (needTerminatingCR) *(*text + textLen - 1) = CR;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetInit
-
- Initialize the net.c module. This function must be called before calling
- any other functions in the module.
-
- Entry: giveTime = pointer to give time function.
- log = pointer to logging function, or nil if none.
- numBuffs = number of stream buffers to preallocate.
-
- Exit: function result = error code.
-
- The give time function is called repeatedly during all network
- operations. It must be declared as follows:
-
- OSErr GiveTime (void)
-
- This function gives time to other processes/threads by calling WaitNextEvent/
- YieldTo[Any]Thread, and may check for user cancel operations (e.g., clicking
- a Cancel button or pressing Command-period or Escape). The function returns
- noErr to continue the operation, or any other error code to cancel (abort)
- the operation. Any error code returned by GiveTime is in turn returned as the
- function result of the functions exported by net.c.
-
- The net.c module is reentrant with respect to the GiveTime function. That is,
- multiple threads of execution may make calls to the net.c module and use
- the YieldTo[Any]Thread call in the GiveTime function.
-
- The logging function is called once for each server command and response
- and stream open/close operation. It must be declared as follows:
-
- void Log (char logEntryType, unsigned long serverAddr,
- unsigned short serverPort, unsigned short localPort, char *str)
-
- You can use this function to log all sever commands and responses. On
- entry to the Log function, the parameters are:
-
- logEntryType =
- 'C' if command.
- 'R' if response.
- ' ' if open/close operation.
- serverAddr = IP address of server.
- serverPort = port number on server.
- localPort = local port number.
- str = command or response string or open/close message string.
-
- The logging function is responsible for filtering out any passwords
- in the strings.
-
- You must call the InitMemUtil function in memutil.c to initialize the
- memory management utilities *before* calling NetInit.
- ----------------------------------------------------------------------------*/
-
- OSErr NetInit (NetGiveTimeFunction giveTime, NetLogFunction log, short numBuffs)
- {
- short i;
- TStreamPtr s;
- OSErr err = noErr;
- long mtu;
-
- gHaveOT = NetHaveOT();
- gGiveTime = giveTime;
- gLog = log;
-
- if (gHaveOT) {
-
- err = InitOpenTransport();
- if (err != noErr) return netOpenDriverErr;
- gStreamBufSize = sizeof(TStream);
-
- } else {
-
- gMacTCPDNRResultProcUPP = NewResultProc(MacTCPDNRResultProc);
- gMacTCPCloseStreamCompletionRoutineUPP =
- NewTCPIOCompletionProc(MacTCPCloseStreamCompletionRoutine);
- err = OpenDriver("\p.IPP", &gRefNum);
- if (err != noErr) return netOpenDriverErr;
- mtu = GetMaxTCPMTU();
- gTCPBufSize = 8 * mtu;
- if (gTCPBufSize < kMinTCPBufSize) gTCPBufSize = kMinTCPBufSize;
- gStreamBufSize = sizeof(TStream) - 1 + gTCPBufSize;
- PatchExitToShell();
-
- }
-
- for (i = 0; i < numBuffs; i++) {
- err = MyNewPtr(gStreamBufSize, &s);
- if (err != noErr) return err;
- s->nextFree = gFree;
- gFree = s;
- }
-
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetIdle
-
- Handle idle time tasks. You must call this function in your main
- event loop.
-
- Exit: function result = error code.
-
- This function takes care of releasing and deallocating streams which
- have finished closing asynchronously in the background.
- ----------------------------------------------------------------------------*/
-
- OSErr NetIdle (void)
- {
- TStreamPtr s, nextClose, prev = nil;
- OSErr err = noErr;
-
- for (s = gClose; s != nil; s = nextClose) {
- nextClose = s->nextClose;
- if (s->release) {
- err = DoTCPRelease(s);
- if (prev == nil) {
- gClose = nextClose;
- } else {
- prev->nextClose = nextClose;
- }
- DisposeStreamBuffer(s);
- } else {
- prev = s;
- }
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetTerm
-
- Terminate the module. You must call this function when you quit.
-
- Exit: function result = error code.
-
- This functions waits until all asynchronous stream close operations have
- completed, or for 5 seconds, whichever happens first. If any close
- operations are still pending after 5 seconds, they are aborted.
-
- In addition, any other open streams are aborted.
- ----------------------------------------------------------------------------*/
-
- OSErr NetTerm (void)
- {
- TStreamPtr s;
- long waitTil;
-
- waitTil = TickCount() + 300;
- while (gClose != nil && TickCount() < waitTil) {
- (*gGiveTime)();
- NetIdle();
- }
- for (s = gAll; s != nil; s = s->next) DoTCPRelease(s);
- while (gFree != nil) {
- s = gFree->next;
- MyDisposePtr(gFree);
- gFree = s;
- }
- gFree = gClose = gAll = nil;
- if (gHaveOT) CloseOpenTransport();
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetOpen
-
- Open a stream.
-
- Entry: addr = IP address of server.
- port = port number of service.
- getHello = true to wait for, receive, and return an initial
- "hello" message.
-
- Exit: function result = error code.
- *stream = opened stream reference.
- *responseCode = server hello message response code.
- response = C-format hello message string, including the
- response code.
-
- Hello message comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- OSErr NetOpen (unsigned long addr, unsigned short port, Boolean getHello,
- NetStreamRef *stream, long *responseCode, CStr255 response)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- err = NewStreamBuffer(&s);
- if (err != noErr) return err;
- s->in = s->out = s->buf;
- s->serverAddr = addr;
- s->serverPort = port;
- *s->command = *s->response = s->responseCode = 0;
- err = DoTCPCreate(s);
- if (err != noErr) goto exit2;
- err = DoTCPActiveOpen(s, addr, port);
- if (err != noErr) goto exit1;
- if (getHello) {
- err = ReadResponse(s, responseCode, response);
- if (err != noErr) goto exit1;
- }
- *stream = (NetStreamRef)s;
- return noErr;
-
- exit1:
-
- if (gHaveOT) {
- if (s->otherSideHasClosed) err = netOpenStreamErr;
- } else {
- if (err == connectionClosing || err == connectionDoesntExist ||
- err == connectionTerminated) err = netOpenStreamErr;
- }
- DoTCPRelease(s);
-
- exit2:
-
- DisposeStreamBuffer(s);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetClose
-
- Close a stream.
-
- Entry: stream = stream reference.
-
- Exit: function result = error code.
-
- A QUIT command is sent to the server before closing the stream.
- Thus you do not need to and should not send this command yourself.
-
- To improve performance, all streams are closed asynchronously.
- This function returns immediately without any delay.
-
- This asynchronous stream closing feature also permits you to close
- connections in the background without interfering with or delaying user
- actions in the foreground.
- ----------------------------------------------------------------------------*/
-
- OSErr NetClose (NetStreamRef stream)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)stream;
- s->closing = true;
-
- /* Link the stream into the queue of closing streams. */
-
- s->nextClose = gClose;
- gClose = s;
-
- /* Send the QUIT command. */
-
- if (gHaveOT) {
-
- if (s->weHaveClosed && s->otherSideHasClosed) {
- s->release = true;
- return noErr;
- } else {
- err = OTSetBlocking(s->ref);
- if (err != noErr) goto exit;
- err = DoTCPSend(s, "QUIT\r\n", 6, true);
- if (err != noErr) goto exit;
- err = OTSetNonBlocking(s->ref);
- if (err != noErr) goto exit;
- }
-
- } else {
-
- s->wds[0].length = 6;
- s->wds[0].ptr = "quit\r\n";
- InitMacTCPParamBlock(&s->pBlock, TCPSend);
- s->pBlock.csParam.send.wdsPtr = (Ptr)s->wds;
- s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) goto exit;
-
- }
-
- if (gLog != nil) {
- LogMessage(s, 'C', "QUIT");
- LogMessage(s, ' ', "Asynch stream close initiated.");
- }
- return noErr;
-
- exit:
-
- s->release = true;
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetCommand
-
- Send a command to a server and get the response.
-
- Entry: stream = stream reference.
- command = C-format command string.
-
- Exit: function result = error code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
- ----------------------------------------------------------------------------*/
-
- OSErr NetCommand (NetStreamRef stream, char *command,
- long *responseCode, CStr255 response)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)stream;
-
- err = SendCommand(s, command);
- if (err != noErr) goto exit;
- err = ReadResponse(s, responseCode, response);
- if (err != noErr) goto exit;
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetExtraResponse
-
- Get an extra command response from a server.
-
- Entry: stream = stream reference.
-
- Exit: function result = error code.
- *responseCode = server response code.
- response = C-format server response string, including the
- response code.
-
- Response comment lines are discarded (response lines with a
- '-' immediately following the response code).
-
- Some servers have commands which return more than one response. The
- NetCommand function above reads the first response. This function is
- used to read the additional response(s). The FTP RETR and STOR commands
- are examples.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetExtraResponse (NetStreamRef stream, long *responseCode,
- CStr255 response)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)stream;
- err = ReadResponse(s, responseCode, response);
- if (err != noErr) goto exit;
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetBatchedCommands
-
- Send multiple batched commands to a server and get and process
- the responses.
-
- Entry: stream = stream reference.
- commands = handle to CR-terminated commands.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
- doOneResponse = pointer to response processing function.
- userDataPtr = pointer to user data to be passed through
- to the doOneResponse function.
-
- Exit: function result = error code.
-
- The response processing function must be declared as follows:
-
- void DoOneResponse (long responseCode, CStr255 response, Ptr userDataPtr)
-
- The function is called once for each command response. It is passed the
- following parameters:
-
- responseCode = server response code.
- response = C-format server response string, including the
- response code.
- userDataPtr = user data pointer.
-
- Response comment lines are discarded (leading response lines with a
- '-' immediately following the response code).
-
- CR line terminators are mapped to CRLF line terminators before sending
- the batched commands.
-
- When you know in advance that you need to send a number of commands to a
- server all in a row, it is usually much more efficient to send them in
- a batch rather than sending them one at a time using the NetCommand function
- above. You should take care, however, since not all servers support batched
- commands.
-
- All of the batched commands must return only a single response (after
- skipping any possible comment lines).
- ----------------------------------------------------------------------------*/
-
- OSErr NetBatchedCommands (NetStreamRef stream, Handle commands,
- NetDoOneResponse doOneResponse, Ptr userDataPtr)
- {
- TStreamPtr s;
- OSErr err = noErr;
- char *p, *q, *r;
- long cmdLen;
- unsigned short len;
- long responseCode;
- CStr255 response;
- short numCmds, i;
- char state;
-
- state = MyHGetState(commands);
-
- err = MungeOut(commands, false);
- if (err != noErr) goto exit;
- cmdLen = MyGetHandleSize(commands);
-
- s = (TStreamPtr)stream;
- MyHLock(commands);
- p = *commands;
- while (cmdLen > 0) {
- len = cmdLen > 4000 ? 4000 : cmdLen;
- q = p + len - 1;
- while (q > p && (*q != LF || *(q-1) != CR)) q--;
- if (q == p) {
- p += len;
- cmdLen -= len;
- continue;
- } else {
- numCmds = 0;
- len = q - p + 1;
- while (q > p) {
- if (*q == LF && *(q-1) == CR) {
- numCmds++;
- q -= 2;
- } else {
- q--;
- }
- }
- }
- err = DoTCPSend(s, StripAddress(p), len, true);
- if (err != noErr) goto exit;
- if (gLog != nil) {
- q = p;
- for (i = 0; i < numCmds; i++) {
- r = q;
- while (*r != CR || *(r+1) != LF) r++;
- *r = 0;
- LogMessage(s, 'C', q);
- q = r + 2;
- }
- }
- p += len;
- cmdLen -= len;
- while (numCmds--) {
- err = ReadResponse(s, &responseCode, response);
- if (err != noErr) goto exit;
- (*doOneResponse)(responseCode, response, userDataPtr);
- }
- }
- MyHSetState(commands, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- MyHSetState(commands, state);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetPutText
-
- Send command text to a server.
-
- Entry: stream = stream reference.
- text = handle to CR-terminated ASCII text lines.
- Warning: the memory block is modified by the function.
- The memory block must be unlocked and nonpurgeable.
-
- Exit: function result = error code.
-
- CR line terminators are translated to CRLF. A terminating "." on a line
- by itself is sent. The caller should not include this terminating line
- in the "text" block. Leading "." characters on lines are mapped to "..".
-
- If the text block does not have a terminating CR at the end of the last
- line, one is added.
- ----------------------------------------------------------------------------*/
-
- OSErr NetPutText (NetStreamRef stream, Handle text)
- {
- TStreamPtr s;
- OSErr err = noErr;
- char *p;
- long textLen;
- unsigned short len;
- char state;
-
- state = MyHGetState(text);
-
- err = MungeOut(text, true);
- if (err != noErr) goto exit;
- textLen = MyGetHandleSize(text);
-
- s = (TStreamPtr)stream;
- MyHLock(text);
- p = *text;
- while (textLen > 0) {
- len = textLen > 4000 ? 4000 : textLen;
- err = DoTCPSend(s, StripAddress(p), len, textLen == len);
- if (err != noErr) goto exit;
- p += len;
- textLen -= len;
- }
- MyHSetState(text, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- MyHSetState(text, state);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetText
-
- Get response text from a server.
-
- Entry: stream = stream reference.
- text = pointer to handle in which to return text,
- or nil if none.
- chunkFunction = pointer to chunk processing function,
- or nil if none.
- userDataPtr = pointer to user data to be passed through
- to the chunk processing function, or nil if none.
-
- Exit: function result = error code.
- *text = handle to received ASCII text lines, if text != nil.
-
- In the text returned via the "text" handle (if requested),
- CRLF line terminators are translated to CR. The server must send a
- terminating "." on a line by itself. This terminating line is not
- included in the "text" handle returned to the caller. Leading
- ".." characters on lines are mapped to ".".
-
- The chunk processing function, if any, is called every time a new
- block of text is received from the server. This function must be
- declared as follows:
-
- OSErr ProcessTextChunk (Ptr t, long tLen, Ptr userDataPtr,
- long *truncPos)
-
- Entry: t = pointer to raw text received from server.
- tLen = length of text received from server.
- userDataPtr = pointer to user data.
-
- Exit: function result = error code.
- *truncPos = position at which to truncate text if
- error code is netTruncatedErr.
-
- If text == nil, the text is processed by the chunk processing function
- in pieces (e.g., saved to a file as it comes in over the network). In
- this case, the text is not accumulated and returned as a whole to the
- NetGetText caller, and the "t" and "tLen" parameters passed to the
- chunk processing function are for just the chunk received.
-
- If text != nil, the text is accumulated as it is received and returned
- as a whole to the NetGetText caller, and the "t" and "tLen" parameters
- passed to the chunk processing function (if any) are for the entire text
- as accumulated so far.
-
- If the chunk processing function returns with an error code, NetGetText
- aborts the stream and returns to the caller immediately with the text handle
- set to just the text received so far. The error code returned by the
- chunk processing function is returned to the NetGetText caller as NetGetText's
- function result.
-
- If the chunk processing function returns netTruncatedErr, and if text != nil,
- the returned text is truncated to "truncPos" bytes, as specified by the
- chunk processing function, and NetGetText returns the error code netTruncatedErr
- to its caller.
-
- The text passed to the chunk processing function is raw. It contains CRLF
- line terminators and doubled leading ".." characters. The terminating "."
- on a line by itself, however, is not passed to the chunk processing function.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetText (NetStreamRef stream, Handle *text,
- NetChunkFunction chunkFunction, Ptr userDataPtr)
- {
- TStreamPtr s;
- OSErr err = noErr;
- OSErr chunkErr = noErr;
- Handle t = nil;
- long tLen = 0;
- long tAllocated = 0;
- unsigned short len;
- char *tEnd;
- long truncPos;
-
- s = (TStreamPtr)stream;
- tLen = s->in - s->out;
- tAllocated = tLen + 4005;
- err = MyNewHandle(tAllocated, &t);
- if (err != noErr) goto exit;
- if (tLen > 0) BlockMoveData(s->out, *t, tLen);
- s->in = s->out = s->buf;
-
- while (true) {
- if (tLen >= 3) {
- tEnd = *t + tLen;
- if (*(tEnd-3) == '.' && *(tEnd-2) == CR && *(tEnd-1) == LF) {
- if (tLen == 3) break;
- if (tLen >= 5 && *(tEnd-5) == CR && *(tEnd-4) == LF) break;
- }
- }
- len = 4000;
- MyHLock(t);
- err = DoTCPRcv(s, *t + tLen, &len);
- if (err != noErr) goto exit;
- MyHUnlock(t);
- tLen += len;
- if (chunkFunction != nil && tLen >= 5) {
- MyHLock(t);
- chunkErr = (*chunkFunction)(*t, tLen - 5, userDataPtr, &truncPos);
- MyHUnlock(t);
- if (chunkErr != noErr) goto exit2;
- }
- if (text == nil) {
- BlockMoveData(*t + tLen - 5, *t, 5);
- tLen = 5;
- } else {
- if (tAllocated - tLen <= 4000) {
- tAllocated += 4000;
- err = MySetHandleSize(t, tAllocated);
- if (err != noErr) goto exit;
- }
- }
- }
-
- if (chunkFunction != nil && tLen > 3) {
- MyHLock(t);
- chunkErr = (*chunkFunction)(*t, tLen-3, userDataPtr, &truncPos);
- MyHUnlock(t);
- if (chunkErr != noErr) goto exit2;
- }
-
- if (text == nil) {
- MyDisposeHandle(t);
- } else {
- err = MungeIn(t, tLen, true);
- if (err != noErr) goto exit;
- *text = t;
- }
-
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- MyDisposeHandle(t);
- return TranslateErrorCode(err);
-
- exit2:
-
- if (text == nil) {
- MyDisposeHandle(t);
- } else {
- if (chunkErr == netTruncatedErr) tLen = truncPos;
- err = MungeIn(t, tLen, true);
- if (err != noErr) goto exit;
- *text = t;
- }
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- return chunkErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataPassiveOpen
-
- Open an FTP passive data stream.
-
- Exit: function result = error code.
- *port = assigned unused local port number.
- *stream = reference to opened FTP data stream.
-
- This function returns immediately. It should be followed by a call to
- NetFTPDataWaitForConnection to wait for the FTP server to open its end of
- the data stream connection.
-
- It's confusing, but this "passive" stream open function is used for
- *active* FTP mode data transfers (the client opens a passive stream
- and waits for the server to connect). For *passive* FTP mode data
- transfers (the server opens a passive stream and waits for the client
- to connect), the regular "active" stream open function NetOpen must
- be used instead.
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataPassiveOpen (unsigned short *port, NetStreamRef *stream)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- err = NewStreamBuffer(&s);
- if (err != noErr) goto exit2;
- s->in = s->out = s->buf;
- err = DoTCPCreate(s);
- if (err != noErr) goto exit2;
- err = DoTCPPassiveOpen(s, port);
- if (err != noErr) goto exit1;
- *stream = (NetStreamRef)s;
- return noErr;
-
- exit1:
-
- DoTCPRelease(s);
-
- exit2:
-
- DisposeStreamBuffer(s);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataClose
-
- Close an FTP data stream.
-
- Entry: stream = FTP data stream reference.
-
- Exit: function result = error code.
-
- This function is used to close all FTP data streams, including "passive
- mode" data streams and "active mode" data streams.
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataClose (NetStreamRef stream)
- {
- TStreamPtr s;
- OSErr err = noErr;
-
- s = (TStreamPtr)stream;
- s->closing = true;
-
- /* Link the stream into the queue of closing streams. */
-
- s->nextClose = gClose;
- gClose = s;
-
- /* Close our end of the stream. */
-
- if (gHaveOT) {
-
- if (!s->weHaveClosed) {
- err = OTSndOrderlyDisconnect(s->ref);
- if (err != noErr) goto exit;
- s->weHaveClosed = true;
- } else if (s->weHaveClosed && s->otherSideHasClosed) {
- s->release = true;
- }
- return noErr;
-
- } else {
-
- /* Start an asynchronous close operation, chained to our
- completion routine. */
-
- InitMacTCPParamBlock(&s->pBlock, TCPClose);
- s->pBlock.ioCompletion = gMacTCPCloseStreamCompletionRoutineUPP;
- s->pBlock.tcpStream = s->tcpStream;
- err = PBControlAsync((ParmBlkPtr)&s->pBlock);
- if (err != noErr) goto exit;
- if (gLog != nil) LogMessage(s, ' ', "Asynch stream close initiated.");
- return noErr;
-
- }
-
- exit:
-
- s->release = true;
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetFTPDataWaitForConnection
-
- Wait for an FTP server to open its end of a passive data stream.
-
- Entry: stream = FTP data stream reference.
-
- Exit: function result = error code.
-
- It's confusing, but this "passive" stream wait function is used for
- *active* FTP mode data transfers (the client opens a passive stream
- and waits for the server to connect). For *passive* FTP mode data
- transfers (the server opens a passive stream and waits for the client
- to connect), the regular "active" stream open function NetOpen must
- be used instead, and this function is not used.
- ----------------------------------------------------------------------------*/
-
- OSErr NetFTPDataWaitForConnection (NetStreamRef stream)
- {
- TStreamPtr s;
- OSErr err = noErr;
- InetAddress *inetAddr;
-
- s = (TStreamPtr)stream;
-
- if (gHaveOT) {
-
- while (true) {
- err = (*gGiveTime)();
- if (err != noErr) goto exit;
- err = OTListen(s->ref, s->call);
- if (err == noErr) break;
- if (err != kOTNoDataErr) goto exit;
- }
- inetAddr = (InetAddress*)s->call->addr.buf;
- s->serverAddr = inetAddr->fHost;
- s->serverPort = inetAddr->fPort;
- s->complete = false;
- err = OTAccept(s->ref, s->ref, s->call);
- if (err != noErr) goto exit;
- err = MyOTStreamWait(s, true);
- if (err != noErr) goto exit;
-
- } else {
-
- while (s->pBlock.ioResult > 0) {
- err = (*gGiveTime)();
- if (err != noErr) goto exit;
- }
- err = s->pBlock.ioResult;
- if (err != noErr) goto exit;
- s->serverAddr = s->pBlock.csParam.open.remoteHost;
- s->serverPort = s->pBlock.csParam.open.remotePort;
-
- }
-
- if (gLog != nil) LogMessage(s, ' ', "Stream opened.");
- return noErr;
-
- exit:
-
- if (gHaveOT) {
- if (s->otherSideHasClosed) err = netOpenStreamErr;
- } else {
- if (err == connectionClosing || err == connectionDoesntExist ||
- err == connectionTerminated) err = netOpenStreamErr;
- }
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetFTPData
-
- Get data from an FTP server.
-
- Entry: stream = FTP data stream reference.
- mapCR = true to map CRLF line terminators to CR.
-
- Exit: function result = error code.
- *data = handle to received data.
-
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetFTPData (NetStreamRef stream, Boolean mapCR, Handle *data)
- {
- TStreamPtr s;
- OSErr err = noErr;
- Handle d = nil;
- long dLen = 0;
- long dAllocated = 4000;
- long numFree;
- unsigned short len;
-
- s = (TStreamPtr)stream;
- err = MyNewHandle(dAllocated, &d);
- if (err != noErr) goto exit;
-
- while (true) {
- numFree = dAllocated - dLen;
- if (numFree <= 4000) {
- dAllocated += 4000;
- err = MySetHandleSize(d, dAllocated);
- if (err != noErr) goto exit;
- }
- len = 4000;
- MyHLock(d);
- err = DoTCPRcv(s, *d + dLen, &len);
- MyHUnlock(d);
- if (err == netLostConnectionErr) break;
- if (err != noErr) goto exit;
- dLen += len;
- }
-
- if (mapCR) {
- err = MungeIn(d, dLen, false);
- if (err != noErr) goto exit;
- } else {
- MySetHandleSize(d, dLen);
- }
-
- *data = d;
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- MyDisposeHandle(d);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetPutFTPData
-
- Send data to an FTP server.
-
- Entry: stream = FTP data stream reference.
- mapCR = true to map CR line terminators to CRLF.
- data = handle to data.
-
- Exit: function result = error code.
-
- Warning: If mapCR = true, the data block is modified by the function,
- and it must be unlocked and nonpurgeable.
- ----------------------------------------------------------------------------*/
-
- OSErr NetPutFTPData (NetStreamRef stream, Boolean mapCR, Handle data)
- {
- TStreamPtr s;
- OSErr err = noErr;
- char *p;
- long dataLen;
- unsigned short len;
- char state;
-
- state = MyHGetState(data);
-
- if (mapCR) {
- err = MungeOut(data, false);
- if (err != noErr) goto exit;
- }
-
- dataLen = MyGetHandleSize(data);
-
- s = (TStreamPtr)stream;
- MyHLock(data);
- p = *data;
- while (dataLen > 0) {
- len = dataLen > 4000 ? 4000 : dataLen;
- err = DoTCPSend(s, StripAddress(p), len, dataLen == len);
- if (err != noErr) goto exit;
- p += len;
- dataLen -= len;
- }
- MyHSetState(data, state);
- return noErr;
-
- exit:
-
- DoTCPRelease(s);
- DisposeStreamBuffer(s);
- MyHSetState(data, state);
- return TranslateErrorCode(err);
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyAddr
-
- Get this Mac's IP address.
-
- Exit: function result = error code.
- *addr = the IP address of this Mac.
-
- With Open Transport, if the Mac has more than one IP interface, the
- IP address of the default interface is returned.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyAddr (unsigned long *addr)
- {
- struct GetAddrParamBlock pBlock;
- OSErr err = noErr, giveTimeErr = noErr;
- static Boolean gotIt = false;
- static unsigned long myAddr;
- InetInterfaceInfo ifaceInfo;
-
- if (!gotIt) {
-
- if (gHaveOT) {
-
- err = OTInetGetInterfaceInfo(&ifaceInfo, kDefaultInetInterface);
- if (err != noErr) return TranslateErrorCode(err);
- myAddr = ifaceInfo.fAddress;
-
- } else {
-
- memset(&pBlock, 0, sizeof(pBlock));
- pBlock.ioResult = 1;
- pBlock.csCode = ipctlGetAddr;
- pBlock.ioCRefNum = gRefNum;
- PBControlAsync((ParmBlkPtr)&pBlock);
- while (pBlock.ioResult > 0) {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- }
- if (err != noErr) return TranslateErrorCode(err);
- err = pBlock.ioResult;
- if (err != noErr) return TranslateErrorCode(err);
- myAddr = pBlock.ourAddress;
-
- }
-
- gotIt = true;
- }
-
- *addr = myAddr;
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyAddrStr
-
- Get this Mac's IP address as a dotted-decimal string
-
- Exit: function result = error code.
- name = this Mac's IP address, as a C-format string.
- You must allocate at least 16 bytes for this string.
- The returned string has max length 15.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyAddrStr (char *addrStr)
- {
- unsigned long addr;
- OSErr err = noErr;
- static char theAddrStr[16];
- static Boolean gotIt=false;
-
- if (!gotIt) {
- err = NetGetMyAddr(&addr);
- if (err != noErr) return TranslateErrorCode(err);
- sprintf(theAddrStr, "%ld.%ld.%ld.%ld",
- (addr >> 24) & 0xff,
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- addr & 0xff);
- gotIt = true;
- }
- strcpy(addrStr, theAddrStr);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetMyName
-
- Get this Mac's domain name, if any.
-
- Exit: function result = error code.
- name = domain name of this Mac, as a C-format string.
- ----------------------------------------------------------------------------*/
-
- OSErr NetGetMyName (CStr255 name)
- {
- unsigned long addr;
- short len;
- static OSErr err = noErr;
- static Boolean gotIt=false;
- static CStr255 theName;
-
- if (!gotIt) {
- err = NetGetMyAddr(&addr);
- if (err != noErr) return TranslateErrorCode(err);
- err = NetAddrToName(addr, theName);
- if (err != noErr) return TranslateErrorCode(err);
- gotIt = true;
- len = strlen(theName);
- if (theName[len-1] == '.') theName[len-1] = 0;
- }
- strcpy(name, theName);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetNameToAddr
-
- Translate a domain name to an IP address.
-
- Entry: name = C-format domain name string, optionally followed by a
- comma, space, or colon and then the port number.
- defaultPort = default port number.
-
- Exit: function result = error code.
- *addr = IP address.
- *port = port number.
- ----------------------------------------------------------------------------*/
-
- OSErr NetNameToAddr (char *name, unsigned short defaultPort,
- unsigned long *addr, unsigned short *port)
- {
- OSErr err = noErr, giveTimeErr = noErr;
- short i;
- static struct {
- CStr255 domainName;
- unsigned long addr;
- } cache[10];
- static short numCache = 0;
- struct hostInfo hInfoMacTCP;
- InetHostInfo hInfoOT;
- Boolean done = false;
- CStr255 domainName;
- char *p, *q;
- TMyOTInetSvcInfo svcInfo;
-
- p = name;
- q = domainName;
- while (*p != 0 && *p != ',' && *p != ' ' && *p != ':') *q++ = *p++;
- *q = 0;
- q = p;
- while (*q == ' ') q++;
- if (*q == 0) {
- *port = defaultPort;
- } else {
- p++;
- if (!isdigit(*p)) return netDNRErr;
- q = p+1;
- while (isdigit(*q)) q++;
- while (*q == ' ') q++;
- if (*q != 0) return netDNRErr;
- *port = atoi(p);
- }
-
- for (i=0; i<numCache; i++) {
- if (strcmp(domainName, cache[i].domainName) == 0) {
- *addr = cache[i].addr;
- return noErr;
- }
- }
-
- if (gHaveOT) {
-
- err = OTInetStringToHost(domainName, addr);
- if (err != noErr) {
- err = MyOTOpenInetServices(&svcInfo);
- if (err == kEINVALErr) return netDNRErr;
- if (err != noErr) return TranslateErrorCode(err);
- svcInfo.complete = false;
- err = OTInetStringToAddress(svcInfo.ref, domainName, &hInfoOT);
- if (err == noErr) err = MyOTInetSvcWait(&svcInfo);
- OTCloseProvider(svcInfo.ref);
- if (err != noErr) {
- if (err == kOTNoDataErr || err == kOTBadNameErr) err = netDNRErr;
- return TranslateErrorCode(err);
- }
- *addr = hInfoOT.addrs[0];
- }
-
- } else {
-
- while (gMacTCPDNROperationInProgress) {
- /* Some other thread is using the DNR. Wait for it to finish. */
- err = (*gGiveTime)();
- if (err != noErr) return err;
- }
- err = OpenResolver(nil);
- if (err != noErr) return TranslateErrorCode(err);
- memset(&hInfoMacTCP, 0, sizeof(hInfoMacTCP));
- gMacTCPDNROperationInProgress = true;
- err = StrToAddr(domainName, &hInfoMacTCP, gMacTCPDNRResultProcUPP, (char*)&done);
- if (err == cacheFault) {
- err = noErr;
- while (!done) {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- }
- if (err == noErr) err = hInfoMacTCP.rtnCode;
- }
- gMacTCPDNROperationInProgress = false;
- (*gGiveTime)();
- CloseResolver();
- if (err != noErr) return TranslateErrorCode(err);
- *addr = hInfoMacTCP.addr[0];
-
- }
-
- if (numCache < 10) {
- strcpy(cache[numCache].domainName, domainName);
- cache[numCache].addr = *addr;
- numCache++;
- }
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetAddrToName
-
- Translate an IP address to a domain name.
-
- Entry: addr = IP address.
-
- Exit: function result = error code.
- name = domain name, as a C-format string.
- ----------------------------------------------------------------------------*/
-
- OSErr NetAddrToName (unsigned long addr, CStr255 name)
- {
- struct hostInfo hInfoMacTCP;
- OSErr err = noErr, giveTimeErr = noErr;
- Boolean done=false;
- TMyOTInetSvcInfo svcInfo;
-
- if (gHaveOT) {
-
- err = MyOTOpenInetServices(&svcInfo);
- if (err != noErr) return TranslateErrorCode(err);
- svcInfo.complete = false;
- err = OTInetAddressToName(svcInfo.ref, addr, name);
- if (err == noErr) err = MyOTInetSvcWait(&svcInfo);
- OTCloseProvider(svcInfo.ref);
- if (err != noErr) {
- if (err == kOTNoDataErr || err == kOTBadNameErr) err = netDNRErr;
- return TranslateErrorCode(err);
- }
- return noErr;
-
- } else {
-
- while (gMacTCPDNROperationInProgress) {
- /* Some other thread is using the DNR. Wait for it to finish. */
- err = (*gGiveTime)();
- if (err != noErr) return err;
- }
-
- err = OpenResolver(nil);
- if (err != noErr) return TranslateErrorCode(err);
- memset(&hInfoMacTCP, 0, sizeof(hInfoMacTCP));
-
- gMacTCPDNROperationInProgress = true;
- err = AddrToName(addr, &hInfoMacTCP, gMacTCPDNRResultProcUPP, (char*)&done);
- if (err == cacheFault) {
- err = noErr;
- while (!done) {
- giveTimeErr = (*gGiveTime)();
- if (err == noErr) err = giveTimeErr;
- }
- if (err == noErr) err = hInfoMacTCP.rtnCode;
- }
- gMacTCPDNROperationInProgress = false;
- (*gGiveTime)();
- CloseResolver();
- if (err != noErr) return TranslateErrorCode(err);
- hInfoMacTCP.cname[254] = 0;
- strcpy(name, hInfoMacTCP.cname);
- return noErr;
-
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetMacTCPDNROperationInProgress
-
- Find out whether there is a MacTCP DNR operation in progress.
-
- Exit: function result = true if MacTCP DNR operation in progress.
- ----------------------------------------------------------------------------*/
-
- Boolean NetMacTCPDNROperationInProgress (void)
- {
- return gMacTCPDNROperationInProgress;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetServerErrInfo
-
- Get server error information for a stream.
-
- Entry: stream = stream reference.
-
- Exit: *serverErrInfo = server error informaton.
- ----------------------------------------------------------------------------*/
-
- void NetGetServerErrInfo (NetStreamRef stream, NetServerErrInfo *serverErrInfo)
- {
- TStreamPtr s;
-
- s = (TStreamPtr)stream;
- strcpy(serverErrInfo->command, s->command);
- strcpy(serverErrInfo->response, s->response);
- serverErrInfo->responseCode = s->responseCode;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetGetStreamStats
-
- Get stream stats (bytes in/out).
-
- Entry: stream = stream reference.
-
- Exit: *bytesIn = number of bytes received on stream.
- *bytesOut = number of bytes sent on stream.
- ----------------------------------------------------------------------------*/
-
- void NetGetStreamStats (NetStreamRef stream, long *bytesIn, long *bytesOut)
- {
- TStreamPtr s;
-
- s = (TStreamPtr)stream;
- *bytesIn = s->bytesIn;
- *bytesOut = s->bytesOut;
- }
-
-
-
- /*----------------------------------------------------------------------------
- NetHaveOT
-
- Determine whether we have Open Transport.
-
- Exit: function result = true if Open Transport and Open Transport/TCP
- are both installed.
-
- Note: Apple says we shouldn't ship 68K OT code yet (not until OT 1.1).
- So this function always returns false on 68K Macs for now.
- ----------------------------------------------------------------------------*/
-
- Boolean NetHaveOT (void)
- {
- static Boolean gotIt = false;
- static Boolean haveOT;
- OSErr err = noErr;
- long result;
-
- #ifndef powerc
- return false;
- #endif
-
- if (!gotIt) {
- err = Gestalt(gestaltOpenTpt, &result);
- haveOT = err == noErr &&
- (result & gestaltOpenTptPresent) != 0 &&
- (result & gestaltOpenTptTCPPresent) != 0;
- gotIt = true;
- }
- return haveOT;
- }
-